using System;
using System.Drawing;
using System.IO;
using System.Net;
using System.Xml.Serialization;

namespace flickr.api.communication
{
    public class FlickrConnection
	{
        private string ApiToken;
        private string ApiKey;
        private string SharedSecret;

        private const string UserAgent = "Mozilla/4.0 FlickrNet API (compatible; MSIE 6.0; Windows NT 5.1)";
        private WebProxy Proxy = WebProxy.GetDefaultProxy();
        private int HttpTimeout = 30000;

	    internal FlickrConnection(string apiKey)
            : this (apiKey, string.Empty) {}

	    internal FlickrConnection(string apiKey, string sharedSecret)
            : this (apiKey, sharedSecret, string.Empty) {}

	    internal FlickrConnection(string apiKey, string sharedSecret, string token)
        {
            if (apiKey == null || apiKey == string.Empty || apiKey.Length == 0)
            {
                throw new ArgumentNullException("ApiKey is required");
            }
            ApiKey = apiKey;
            SharedSecret = sharedSecret;
            ApiToken = token;
        }

//        public AuthLevel CurrentAuthorization
//        {
//            
//        }

        public bool IsAuthorized
        {
            get { return SharedSecret != string.Empty; }
        }

        public string GetAuthorizationUrl(string frob)
        {
            string authLevel = "read";
            string frobHash = frob != string.Empty ? string.Format("frob{0}", frob) : string.Empty;
            string url = "http://www.flickr.com/services/auth/?api_key=" + ApiKey + "&perms=" + authLevel;
            if (frobHash != string.Empty)
            {
                url += "&frob=" + frob;
            }
            string hash = string.Format("{0}api_key{1}{2}perms{3}", SharedSecret, ApiKey, frobHash, authLevel);
            return url + "&api_sig=" + Utilities.Md5Hash(hash);
        }

        public FlickrResponse SendFlickrRequest(FlickrRequest request)
        {
            FlickrResponse flickrResponse = GetFlickrResponse(request);
	
            if( flickrResponse.Status != ResponseStatus.OK )
            {
                throw new FlickrException(
                    flickrResponse.Error.Code,
                    flickrResponse.Error.Message
                );
            }
            request.Reset();
            return flickrResponse;
        }

        protected FlickrResponse GetFlickrResponse(FlickrRequest request)
        {
            string url = request.BuildRequestUrl(ApiKey, SharedSecret, ApiToken);

            return DeserializeFlickrResponse( SendHttpRequest(url) );
        }


	    private HttpWebResponse SendHttpRequest(string url)
        {
            HttpWebRequest httpWebRequest = null;
            HttpWebResponse httpWebResponse = null;

            try
            {
                httpWebRequest = BuildHttpRequest(url);
                httpWebResponse = (HttpWebResponse)httpWebRequest.GetResponse();
            }
            catch(WebException webException)
            {
                ThrowConnectionException(webException);
            }

            return httpWebResponse;
        }

	    private HttpWebRequest BuildHttpRequest(string url)
	    {
	        HttpWebRequest req;
	        req = (HttpWebRequest)HttpWebRequest.Create(url);
	        req.Method = "POST";
	        req.ContentLength = 0;
	        req.UserAgent = UserAgent;
	        req.Proxy = Proxy;
	        req.Timeout = HttpTimeout;
	        return req;
	    }

	    private void ThrowConnectionException(WebException webException)
	    {
	        if( webException.Status == WebExceptionStatus.ProtocolError )
	        {
	            HttpWebResponse res2 = (HttpWebResponse)webException.Response;
	            if( res2 != null )
	            {
	                throw new FlickrException((int)res2.StatusCode, res2.StatusDescription);
	            }
	        }
	        throw new FlickrException(9999, webException.Message);
	    }

	    private FlickrResponse DeserializeFlickrResponse(HttpWebResponse res)
	    {
            string xmlSource = string.Empty;
            try {
                using (StreamReader sr = new StreamReader(res.GetResponseStream()))
                {
                    xmlSource = sr.ReadToEnd();
                    StringReader responseReader = new StringReader(xmlSource);
                    FlickrResponse flickrResponse = (FlickrResponse)new XmlSerializer(typeof(FlickrResponse)).Deserialize(responseReader);
                    responseReader.Close();

                    return flickrResponse;
                }
	        }
	        catch(InvalidOperationException ex)
	        {
                string message = string.Format("Invalid response received ({0})\nXml Source: '{1}'", ex.Message, xmlSource);
	            throw new FlickrException(9998, message);
	        }
	    }

        public Image DownloadImageFromUrl(string url)
        {
            return Bitmap.FromStream(SendHttpRequest(url).GetResponseStream());
        }
	}
}